// Copyright FreeHEP, 2005-2007.
package org.freehep.maven.nar;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import org.apache.bcel.classfile.ClassFormatException;
import org.apache.bcel.classfile.JavaClass;
import org.apache.bcel.classfile.Method;
import org.apache.maven.artifact.DependencyResolutionRequiredException;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
import org.codehaus.plexus.compiler.util.scan.InclusionScanException;
import org.codehaus.plexus.compiler.util.scan.SourceInclusionScanner;
import org.codehaus.plexus.compiler.util.scan.StaleSourceScanner;
import org.codehaus.plexus.compiler.util.scan.mapping.SingleTargetSourceMapping;
import org.codehaus.plexus.compiler.util.scan.mapping.SuffixMapping;
import org.codehaus.plexus.util.FileUtils;
import org.codehaus.plexus.util.StringUtils;
/**
* Sets up the javah configuration
*
* @author <a href="Mark.Donszelmann@slac.stanford.edu">Mark Donszelmann</a>
* @version $Id: Javah.java 13111 2007-07-24 04:02:00Z duns $
*/
public class Javah {
/**
* Javah command to run.
*
* @parameter default-value="javah"
*/
private String name = "javah";
/**
* Add boot class paths. By default none.
*
* @parameter
*/
private List/*<File>*/ bootClassPaths = new ArrayList();
/**
* Add class paths. By default the classDirectory directory is included and all dependent classes.
*
* @parameter
*/
private List/*<File>*/ classPaths = new ArrayList();
/**
* The target directory into which to generate the output.
*
* @parameter expression="${project.build.directory}/nar/javah-include"
* @required
*/
private File jniDirectory;
/**
* The class directory to scan for class files with native interfaces.
*
* @parameter expression="${project.build.directory}/classes"
* @required
*/
private File classDirectory;
/**
* The set of files/patterns to include
* Defaults to "**\/*.class"
*
* @parameter
*/
private Set includes = new HashSet();
/**
* A list of exclusion filters.
*
* @parameter
*/
private Set excludes = new HashSet();
/**
* The granularity in milliseconds of the last modification
* date for testing whether a source needs recompilation
*
* @parameter default-value="0"
* @required
*/
private int staleMillis = 0;
/**
* The directory to store the timestampfile for the processed aid files. Defaults to jniDirectory.
*
* @parameter
*/
private File timestampDirectory;
/**
* The timestampfile for the processed class files. Defaults to name of javah.
*
* @parameter
*/
private File timestampFile;
private AbstractCompileMojo mojo;
public Javah() {
}
public void setAbstractCompileMojo(AbstractCompileMojo mojo) {
this.mojo = mojo;
}
protected List getClassPaths() throws MojoExecutionException {
if (classPaths.isEmpty()) {
try {
classPaths.addAll(mojo.getMavenProject().getCompileClasspathElements());
} catch (DependencyResolutionRequiredException e) {
throw new MojoExecutionException("JAVAH, cannot get classpath", e);
}
}
return classPaths;
}
protected File getJniDirectory() {
if (jniDirectory == null) {
jniDirectory = new File(mojo.getMavenProject().getBuild().getDirectory(), "nar/javah-include");
}
return jniDirectory;
}
protected File getClassDirectory() {
if (classDirectory == null) {
classDirectory = new File(mojo.getMavenProject().getBuild().getDirectory(), "classes");
}
return classDirectory;
}
protected Set getIncludes() {
if (includes.isEmpty()) {
includes.add("**/*.class");
}
return includes;
}
protected File getTimestampDirectory() {
if (timestampDirectory == null) {
timestampDirectory = getJniDirectory();
}
return timestampDirectory;
}
protected File getTimestampFile() {
if (timestampFile == null) {
timestampFile = new File(name);
}
return timestampFile;
}
public void execute() throws MojoExecutionException, MojoFailureException {
getClassDirectory().mkdirs();
try {
SourceInclusionScanner scanner = new StaleSourceScanner(staleMillis, getIncludes(), excludes);
if (getTimestampDirectory().exists()) {
scanner.addSourceMapping(new SingleTargetSourceMapping( ".class", getTimestampFile().getPath() ));
} else {
scanner.addSourceMapping(new SuffixMapping( ".class", ".dummy" ));
}
Set classes = scanner.getIncludedSources(getClassDirectory(), getTimestampDirectory());
if (!classes.isEmpty()) {
Set files = new HashSet();
for (Iterator i=classes.iterator(); i.hasNext(); ) {
String file = ((File)i.next()).getPath();
JavaClass clazz = NarUtil.getBcelClass(file);
Method[] method = clazz.getMethods();
for (int j=0; j<method.length; j++) {
if (method[j].isNative()) files.add(clazz.getClassName());
}
}
if (!files.isEmpty()) {
getJniDirectory().mkdirs();
getTimestampDirectory().mkdirs();
mojo.getLog().info( "Running "+name+" compiler on "+files.size()+" classes...");
int result = NarUtil.runCommand(name, generateArgs(files), null, mojo.getLog());
if (result != 0) {
throw new MojoFailureException(name+" failed with exit code "+result+" 0x"+Integer.toHexString(result));
}
FileUtils.fileWrite(getTimestampDirectory()+"/"+getTimestampFile(), "");
}
}
} catch (InclusionScanException e) {
throw new MojoExecutionException( "JAVAH: Class scanning failed", e );
} catch (IOException e) {
throw new MojoExecutionException( "JAVAH: IO Exception", e );
} catch (ClassFormatException e) {
throw new MojoExecutionException( "JAVAH: Class could not be inspected", e);
}
}
private String[] generateArgs(Set/*<String>*/ classes) throws MojoExecutionException {
List args = new ArrayList();
if (!bootClassPaths.isEmpty()) {
args.add("-bootclasspath");
args.add(StringUtils.join(bootClassPaths.iterator(), File.pathSeparator));
}
args.add("-classpath");
args.add(StringUtils.join(getClassPaths().iterator(), File.pathSeparator));
args.add("-d");
args.add(getJniDirectory().getPath());
if (mojo.getLog().isDebugEnabled()) {
args.add("-verbose");
}
if (classes != null) {
for (Iterator i = classes.iterator(); i.hasNext(); ) {
args.add((String)i.next());
}
}
return (String[])args.toArray(new String[args.size()]);
}
}